﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;

// Inspired by Kent Boogaart, http://stackoverflow.com/questions/1083224/pushing-read-only-gui-properties-back-into-viewmodel
// this class gives access to the widths taken by the elements in an ItemsControl
// its Widths property is assumed to be empty and is filled when the ItemsControl is loaded. 


namespace Elca.MvvmHelpers {
    public static class ItemsControlSizeObserver {

        private static readonly Dictionary<ObservableCollection<double>, ItemsControl> s_widthsMap = new Dictionary<ObservableCollection<double>, ItemsControl>();

        public static readonly DependencyProperty IsWidthReadyProperty = DependencyProperty.RegisterAttached(
            "IsWidthReady",
            typeof(bool),
            typeof(ItemsControlSizeObserver));

        public static readonly DependencyProperty WidthsProperty = DependencyProperty.RegisterAttached(
            "Widths",
            typeof(ObservableCollection<double>),
            typeof(ItemsControlSizeObserver), 
            new FrameworkPropertyMetadata(OnWidthsChanged));

        public static bool GetIsWidthReady(ItemsControl ic) {

            return (bool)ic.GetValue(IsWidthReadyProperty);
        }

        public static void SetIsWidthReady(ItemsControl ic, bool value) {

            ic.SetValue(IsWidthReadyProperty, value);
        }

        public static ObservableCollection<double> GetWidths(ItemsControl ic) {

            return (ObservableCollection<double>)ic.GetValue(WidthsProperty);
        }

        public static void SetWidths(ItemsControl ic, ObservableCollection<double> observedWidths) {

            ic.SetValue(WidthsProperty, observedWidths);
        }

        private static void OnWidthsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) {

            var ic = (ItemsControl) dependencyObject;
            ic.Loaded += OnItemsControlLoaded;

            if (e.OldValue != null) {
                ObservableCollection<double> widths = (ObservableCollection<double>) e.OldValue;
                widths.CollectionChanged -= OnWidthCollectionChanged;
            }
        }

        private static void OnItemsControlLoaded(object sender, RoutedEventArgs e) {
            var ic = (ItemsControl) sender;
            ic.Loaded -= OnItemsControlLoaded;

            ObservableCollection<double> widths = GetWidths(ic);

            FillWidths(ic, widths);

            s_widthsMap[widths] = ic;
            widths.CollectionChanged += OnWidthCollectionChanged;
            SetIsWidthReady(ic, true);
        }

        private static void OnWidthCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {

            if (e.Action == NotifyCollectionChangedAction.Replace) {

                ObservableCollection<double> widths = (ObservableCollection<double>) sender;
                ItemsControl ic = s_widthsMap[widths];

                DependencyObject obj = ic.ItemContainerGenerator.ContainerFromIndex(e.NewStartingIndex);

                FrameworkElement fe = (FrameworkElement) obj;
                
                fe.Width = widths[e.NewStartingIndex];
            }
        }

        private static void FillWidths(ItemsControl ic, ObservableCollection<double> widths) {

            widths.Clear();
            
            for (int i = 0; i < ic.Items.Count; i++) {

                DependencyObject obj = ic.ItemContainerGenerator.ContainerFromIndex(i);

                FrameworkElement fe = (FrameworkElement) obj;
                
                widths.Add(fe.ActualWidth);
            }
        }
    }
}
